﻿using System;
using Castle.DynamicProxy;
using System.Linq;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Common.LogHelper;
using Newtonsoft.Json;

namespace Core.Extensions.AOP
{
    /// <summary>
    /// 拦截器 继承Intercepter接口
    /// </summary>
    public class LogAOP : IInterceptor
    {
        /// <summary>
        /// 实例化IIntercepter唯一方法
        /// </summary>
        /// <param name="invocation">包含被拦截的方法信息</param>
        public void Intercept(IInvocation invocation)
        {
            //记录被拦截方法信息的日志信息
            var dataIntercept = $"{DateTime.Now.ToString("yyyyMMddHHmmss")} " +
                                $"当前执行的方法：{invocation.Method.Name} " +
                                $"参数是：{string.Join("@ ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray())} \r\n";
            try
            {
                //在被拦截的方法执行完毕后 继续执行当前方法
                invocation.Proceed();

                if (IsAsyncMethod(invocation.Method))
                {
                    #region 方案一
                    if (invocation.Method.ReturnType == typeof(Task))
                    {
                        invocation.ReturnValue = InternalAsyncHelper.awaitTaskWithPostActionAndFinally(
                            (Task)invocation.ReturnValue,
                            async () => await SuccessAction(invocation, dataIntercept),/*成功时执行*/
                            ex =>
                            {
                                LogEx(ex, dataIntercept);
                            });
                    }
                    else//Task<TResult>
                    {
                        invocation.ReturnValue = InternalAsyncHelper.callAwaitTaskWithPostActionAndFinallyAndGetResult(
                                invocation.Method.ReturnType.GenericTypeArguments[0],
                                invocation.ReturnValue,
                                async (o) => await SuccessAction(invocation, dataIntercept, o),/*成功时执行*/
                                ex =>
                                {
                                    LogEx(ex, dataIntercept);
                                }
                            );
                    }
                    #endregion

                    //方案一不行，试试这个方案
                    #region 方案二
                    //var type = invocation.Method.ReturnType;
                    //var resultProperty = type.GetProperty("Result");
                    //dataIntercept += ($"【执行完成结果】：{JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue))}");
                    //Parallel.For(0, 1, e =>
                    //{
                    //    LogLock.OutSql2Log("AOPLog", new string[] { dataIntercept });
                    //});
                    #endregion
                }
                else
                {//同步1
                    dataIntercept += ($"【执行完成结果】:{invocation.ReturnValue}");
                    Parallel.For(0, 1, e =>
                    {
                        LogLock.OutSql2Log("AOPLog", new string[] { dataIntercept });
                    });
                }
            }
            catch(Exception ex)//同步2
            {
                LogEx(ex, dataIntercept);
            }
            //dataIntercept += ($"被拦截方法执行完毕，返回结果：{invocation.ReturnValue}");

            //#region 输出到当前项目日志
            //var logPath = Directory.GetCurrentDirectory() + "/Log";
            //if (!Directory.Exists(logPath))
            //{
            //    Directory.CreateDirectory(logPath);
            //}
            //string filename = logPath + $"/InterceptLog-{DateTime.Now.ToString("yyyyMMddHHmmss")}.log";
            //StreamWriter sw = File.AppendText(filename);
            //sw.WriteLine(dataIntercept);
            //sw.Close();
            //#endregion
        }
        private async Task SuccessAction(IInvocation invocation, string dataIntercept, object o = null)
        {
            dataIntercept  += ($"【执行完成结果】：{JsonConvert.SerializeObject(o)}");

            await Task.Run(()=> {
                Parallel.For(0, 1, e =>
                {
                    LogLock.OutSql2Log("AOPLog", new string[] { dataIntercept });
                });
            });
        }

        private void LogEx(Exception ex, string dataIntercept)
        {
            if (ex != null)
            {
                //执行的service中，收录异常
                //MiniProfiler.Current.CustomTiming("Errors：", ex.Message);
                //执行的Service中，捕获异常
                dataIntercept += ($"【执行完成结果】：方法中出现异常：{ex.Message + ex.InnerException}\r\n");
                Parallel.For(0, 1, e => {
                    LogLock.OutSql2Log("AOPLog", new string[] { dataIntercept });
                });
            }
        }

        public static bool IsAsyncMethod(MethodInfo method)
        {
            return (
                method.ReturnType == typeof(Task) ||
                (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
                );
        }
    }

    internal static class InternalAsyncHelper
    {
        public static async Task awaitTaskWithPostActionAndFinally(Task actualReturnValue, Func<Task> postAction, Action<Exception> finalAction)
        {
            Exception exception = null;
            try
            {
                await actualReturnValue;
                await postAction();
            }
            catch (Exception ex)
            {
                exception = ex;
            }
            finally
            {
                finalAction(exception);
            }
        }

        public static async Task<T> awaitTaskWithPostActionAndGetResult<T>(Task<T> actualReturnValue, Func<object, Task> postAction, Action<Exception> finalAction)
        {
            Exception exception = null;
            try
            {
                var result = await actualReturnValue;
                await postAction(result);
                return result;
            }
            catch (Exception ex)
            {
                exception = ex;
                throw;
            }
            finally
            {
                finalAction(exception);
            }
        }

        public static object callAwaitTaskWithPostActionAndFinallyAndGetResult(Type taskReturnType, object actualReturnValue, Func<object, Task> action, Action<Exception> finalAction)
        {
            return typeof(InternalAsyncHelper).GetMethod("awaitTaskWithPostActionAndGetResult", BindingFlags.Public | BindingFlags.Static)
                .MakeGenericMethod(taskReturnType)
                .Invoke(null, new object[] { actualReturnValue, action, finalAction });
        }
    }
}
